package com.dirox.jira.notes.service.imp;

import com.atlassian.activeobjects.external.ActiveObjects;
import com.atlassian.sal.api.user.UserManager;
import com.atlassian.sal.api.user.UserProfile;
import com.dirox.jira.notes.entities.Issue;
import com.dirox.jira.notes.entities.Note;
import com.dirox.jira.notes.entities.TaggedIssue;
import com.dirox.jira.notes.entities.TaggedUser;
import com.dirox.jira.notes.entities.User;
import com.dirox.jira.notes.service.NoteService;
import com.google.common.collect.ImmutableMap;
import static com.google.common.collect.Lists.newArrayList;
import net.java.ao.Query;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;


public final class NoteServiceImp implements NoteService {
	private ActiveObjects ao;
	private UserManager salUserManager;

	public void setAo(ActiveObjects ao) {
		this.ao = ao;
	}

	public void setSalUserManager(UserManager salUserManager) {
		this.salUserManager = salUserManager;
	}

	@Override
	public Note add(String content, int priority, User currentUser) {
		final Note note = ao.create(
			Note.class,
			ImmutableMap.<String, Object>of(
				"OWNER_ID",
				currentUser,
				"CREATED_DATE",
				new Date(),
				"STATUS",
				true));
		note.setContent(content);
		note.setPriority(priority);
		note.save();

		return note;
	}

	@Override
	public void tagNoteToUser(Note note,
	                          com.atlassian.crowd.embedded.api.User user) {
		User u = getOrCreateUser(user.getName(),
		                         user.getDisplayName());
		final TaggedUser taggedUser = ao.create(TaggedUser.class);
		taggedUser.setNote(note);
		taggedUser.setUser(u);
		taggedUser.save();
	}

	@Override
	public void untagNoteToUser(Note note, User user) {
		TaggedUser[] taggedUsers = ao.find(
			TaggedUser.class,
			Query.select().where("NOTE_ID = ? AND USER_ID = ?", note, user));

		if (taggedUsers.length > 0) {
			ao.delete(taggedUsers[0]);
		}
	}

	@Override
	public void associateUsers(Note note,
	                           com.atlassian.crowd.embedded.api.User[] arrUsers) {
		boolean isDuplicated = false;
		User[] old_users = note.getUsers();

		for (User u : old_users) {
			isDuplicated = false;

			for (com.atlassian.crowd.embedded.api.User user : arrUsers) {
				if (u.getUserName()
					     .equals(user.getName())) {
					isDuplicated = true;

					break;
				}
			}

			// untagged user from note
			if (isDuplicated == false) {
				untagNoteToUser(note, u);
			}
		}

		for (com.atlassian.crowd.embedded.api.User user : arrUsers) {
			isDuplicated = false;

			for (User u : old_users) {
				if (u.getUserName()
					     .equals(user.getName())) {
					isDuplicated = true;

					break;
				}
			}

			// tagged user from note
			if (isDuplicated == false) {
				tagNoteToUser(note, user);
			}
		}
	}

	@Override
	public void tagNoteToIssue(Note note, com.atlassian.jira.issue.Issue issue) {
		Issue i = getOrCreateIssue(issue.getKey(),
		                           issue.getSummary());
		final TaggedIssue taggedIssue = ao.create(TaggedIssue.class);
		taggedIssue.setNote(note);
		taggedIssue.setIssue(i);
		taggedIssue.save();
	}

	// Untagged note and memo to issue
	@Override
	public void untagNoteToIssue(Note note, Issue issue) {
		TaggedIssue[] taggedIssues = ao.find(
			TaggedIssue.class,
			Query.select().where("NOTE_ID = ? AND ISSUE_ID = ?", note, issue));

		if (taggedIssues.length > 0) {
			ao.delete(taggedIssues[0]);
		}
	}

	@Override
	public void associateIssues(Note note,
	                            com.atlassian.jira.issue.Issue[] arrIssues) {
		boolean isDuplicated = false;
		Issue[] old_issues = note.getIssues();

		for (Issue i : old_issues) {
			isDuplicated = false;

			for (com.atlassian.jira.issue.Issue issue : arrIssues) {
				if (i.getKey()
					     .equals(issue.getKey())) {
					isDuplicated = true;

					break;
				}
			}

			// unatagged note to isse
			if (isDuplicated == false) {
				untagNoteToIssue(note, i);
			}
		}

		for (com.atlassian.jira.issue.Issue issue : arrIssues) {
			isDuplicated = false;

			for (Issue i : old_issues) {
				if (i.getKey()
					     .equals(issue.getKey())) {
					isDuplicated = true;

					break;
				}
			}

			// tagged note to issue
			if (isDuplicated == false) {
				tagNoteToIssue(note, issue);
			}
		}
	}

	@Override
	public void delete(int id) {
		Note note = ao.get(Note.class, id);
		note.setStatus(false);
		note.save();
	}

	@Override
	public Note update(int id, String content, int priority) {
		Note note = ao.get(Note.class, id);
		note.setContent(content);

		if (priority != -1) {
			note.setPriority(priority);
		}

		note.save();

		return note;
	}

	@Override
	public List<Note> getAll() {
		return newArrayList(
			ao.find(
				Note.class,
				Query.select().where("STATUS = ?", Note.STATUS_IN_USE)));
	}

	@Override
	public List<Note> getNotes(int priority, User currentUser) {
		if (priority == -1) {
			return newArrayList(
				ao.find(
					Note.class,
					Query.select().where(
						"OWNER_ID = ? AND STATUS = ?",
						currentUser,
						Note.STATUS_IN_USE)));
		}

		return newArrayList(
			ao.find(
				Note.class,
				Query.select().where(
					"OWNER_ID = ? AND STATUS = ? AND PRIORITY = ?",
					currentUser,
					Note.STATUS_IN_USE,
					priority)));
	}

	@Override
	public List<Note> getNotes(String issueKey, User currentUser) {
		List<Note> notes = new ArrayList<Note>();

		for (Note note : getNotes(-1, currentUser)) {
			for (Issue issue : note.getIssues()) {
				if (issue.getKey()
					         .equals(issueKey)) {
					notes.add(note);
				}
			}
		}

		return notes;
	}

	@Override
	public List<Note> getMemos(User currentUser) {
		List<Note> memos = new ArrayList<Note>();

		for (Note note : getAll()) {
			for (User user : note.getUsers()) {
				if (user.getUserName()
					        .equals(currentUser.getUserName())) {
					memos.add(note);
				}
			}
		}

		return memos;
	}

	@Override
	public List<Note> getMemos(String issueKey, User currentUser) {
		List<Note> memos = new ArrayList<Note>();

		for (Note note : getAll()) {
			for (User user : note.getUsers()) {
				if (user.getUserName()
					        .equals(currentUser.getUserName())) {
					for (Issue each : note.getIssues()) {
						if (each.getKey()
							        .equals(issueKey)) {
							memos.add(note);
						}
					}
				}
			}
		}

		return memos;
	}

	@Override
	public Note getNote(int id) {
		return ao.get(Note.class, id);
	}

	public User currentUser() {
		UserProfile profile = salUserManager.getUserProfile(
			salUserManager.getRemoteUsername());

		return getOrCreateUser(profile.getUsername(),
		                       profile.getFullName());
	}

	private User getOrCreateUser(String userName, String displayName) {
		User[] users = ao.find(
			User.class,
			Query.select().where("USER_NAME = ?", userName));

		if (users.length == 0) {
			return createUser(userName, displayName);
		} else if (users.length == 1) {
			// update user
			users[0].setDisplayName(displayName);
			users[0].save();

			return users[0];
		} else {
			throw new IllegalStateException(
				"Found multiple users for username: " + userName);
		}
	}

	private User createUser(String userName, String displayName) {
		return ao.create(
			User.class,
			ImmutableMap.<String, Object>of(
				"USER_NAME",
				userName,
				"DISPLAY_NAME",
				displayName));
	}

	private Issue getOrCreateIssue(String key, String summary) {
		Issue[] issues = ao.find(
			Issue.class,
			Query.select().where("KEY = ?", key));

		if (issues.length == 0) {
			return createIssue(key, summary);
		} else if (issues.length == 1) {
			// update issue
			issues[0].setSummary(summary);
			issues[0].save();

			return issues[0];
		} else {
			throw new IllegalStateException(
				"Found multiple issues for key: " + key);
		}
	}

	private Issue createIssue(String key, String summary) {
		return ao.create(
			Issue.class,
			ImmutableMap.<String, Object>of("KEY", key, "SUMMARY", summary));
	}
}
